package at.fhhgb.mc.Aufgabe02;

public class ChainingHashSet {

	/**
	 * Array which stores overflow lists that are indexed by the hash code of their elements.
	 */
	private RandomAccessDoubleLinkedList[] array;

	public ChainingHashSet() {
		this.array = new RandomAccessDoubleLinkedList[10]; // array with default size
		for (int i = 0; i < array.length; i++) {
			array[i] = new RandomAccessDoubleLinkedList(); // creates a new list in each array element
		}
	}

	/**
	 * Initializes an empty hashtable with the given number of overflow lists. If the index size is smaller than 0, a default size of 10
	 * applies.
	 */
	public ChainingHashSet(int indexSize) {
		if (indexSize > 0) {
			this.array = new RandomAccessDoubleLinkedList[indexSize]; // custom size
		} else {
			this.array = new RandomAccessDoubleLinkedList[10]; // if the given number is invalid, the default size applies
		}

		for (int i = 0; i < array.length; i++) {
			array[i] = new RandomAccessDoubleLinkedList(); // creates a new list in each array element
		}
	}

	/**
	 * Inserts a new element val into the hashtable (hashcode = val % array.length), if it did not exist in the table before. Returns true
	 * if a new element was inserted, false if it already existed.
	 * 
	 * @param val
	 *            Value that will be inserted
	 * @return Returns true if the insertion was successful, false otherwise
	 */
	public boolean insert(int val) {
		int hashCode = val % this.array.length; // calculates the hashcode

		if (!this.contains(val)) { // checks if its already in the list
			array[hashCode].pushBack(val); // pushes the new value 
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Returns true if the given value is already stored in the hashtable, false otherwise.
	 * 
	 * @param val
	 *            Value that will be searched
	 * @return Returns true if the value is already in the table, false otherwise
	 */
	public boolean contains(int val) {
		int hashCode = val % this.array.length; // calculates the hashcode of the given number

		return array[hashCode].contains(val); // applies the contains-method of the RandomAccessDoubleLinkedList class and returns true or false
	}

	/**
	 * Removes the given element from the hashtable if it exists. Returns true if the element was removed, false if no such element existed.
	 * 
	 * @param val
	 *            Element which will be removed
	 * @return Returns true if successful, false otherwise
	 */
	public boolean remove(int val) {
		if (!this.contains(val)) {
			return false;
		} else {
			int hashCode = val % this.array.length; // calculates hashcode

			array[hashCode].removeAll(val); // removes the value. As long as every number can only stored once in the list, the remove-all method works
			return true;
		}

	}

	/**
	 * Counts the amount of values, which are stored in the same overflow list
	 * 
	 * @param hashVal
	 *            Position in the overflow list
	 * @return Returns the amount of values, which are stored in the same overflow list
	 */
	public int getOverflowCount(int hashVal) {
		if (hashVal > -1 && hashVal < this.array.length) { // checks for valid hashcode
			return array[hashVal].numberOfElements(); // calls the helper method numberOfElements of the RandomAccessDoubleLinkedList-class to count the elements of a list (without dummy nodes!!)
		} else {
			return Integer.MIN_VALUE;
		}

	}

	/**
	 * Counts the amount of elements in the hashtable by adding the amount of elements in each overflow list.
	 * 
	 * @return Returns the amount of elements in the hashtable
	 */
	public int elements() {
		int numberOfElements = 0;

		for (int i = 0; i < array.length; i++) {
			numberOfElements += array[i].numberOfElements(); // counts the number of all elements in the array
		}
		return numberOfElements;
	}

}
